/***************************************************************
*
* M3D_FLOAT2INT_VMX.H
*
* Float-2-int conversions
***************************************************************/

#pragma once
#define _M3D_FLOAT2INT_VMX_H_

#include <m3d/m3d_vmx.h>


/***************************************************************
* m3dPrepareCompressVert ()
***************************************************************/
FORCE_INLINE void m3dPrepareDecompressVert (const INT16 *vertOffset, const INT16 *vertScale, vmxV &offset, vmxV &scale)
{
   vmxV _scale  = vmxLoadShort4(vertScale);
   vmxV _offset = vmxLoadShort4(vertOffset);

   static const vmxV c_scl  = vmxVectSet(32767.f, 32767.f, 32767.f, 0.f);

   _offset = (_offset / _scale) * c_scl;

   offset = vmxVectRound(_offset);
   scale  = vmxVectSetW(_scale, 0.f);
   return;
}
/***************************************************************
* m3dDecompressVert ()
***************************************************************/
FORCE_INLINE void m3dDecompressVert_4 (vmxV offset, vmxV scale, INT16 *v, m4dV  *_v)
{
   static const vmxV c_scl = vmxVectSet(1.f/32767.f, 1.f/32767.f, 1.f/32767.f, 0.f);
   
   vmxV vIn  = vmxLoadShort4(v);
   vmxV vOut = (vIn + offset) * c_scl * scale;
   vmxVectStoreUA(vOut, (float*)_v);
   return;
}
/***************************************************************
* m3dDecompressVert ()
***************************************************************/
FORCE_INLINE void m3dDecompressVert (vmxV offset, vmxV scale, INT16 *v, m3dV  *_v)
{
   static const vmxV c_scl = vmxVectSet(1.f/32767.f, 1.f/32767.f, 1.f/32767.f, 0.f);
   vmxV vIn  = vmxLoadShort4(v);
   vmxV vOut = (vIn + offset) * c_scl * scale;
   vmxVectStore3UA(vOut, (float*)_v);
   return;
}

/***************************************************************
* m3dCompressNormal_FP32_VMX_4A ()
***************************************************************/
FORCE_INLINE void m3dCompressNormal_FP32_VMX_4A (const m4dV *pnorm, float *_norm)
{
   static const vmxV c_minus_one           = vmxVectSet(-1.f, -1.f, -1.f, -1.f);
   static const vmxV c_half_mul_256        = vmxVectSet(256.f*0.5f, 256.f*0.5f, 256.f*0.5f, 256.f*0.5f);
   static const vmxV c_almost_half_mul_256 = vmxVectSet(256.f*0.4999f, 256.f*0.4999f, 256.f*0.4999f, 0.f);
   static const vmxV c_dot_256             = vmxVectSet( 1.f, 256.f, 256.f*256.f, 0.f);

   vmxV norm = vmxVectLoadA((float*)pnorm);
        norm = vmxVectMax(norm, c_minus_one);
        norm = vmxVectMin(norm, vmxVectOne());

        norm = vmxVectMadd(norm, c_almost_half_mul_256, c_half_mul_256);
        norm = vmxVectTrunc(norm);
   vmxV res  = vmxVectDot(norm, c_dot_256);

   *_norm = vmxVectGetX(res);

#ifdef _DEBUG
   m3dV newNorm;
   m3dDecompressNormal_FP32(_norm, &newNorm);
   ASSERT(m3dAngleVector_COS(pnorm, &newNorm) > 0.96f);
#endif
   return;
}

/***************************************************************
* m3dDecompressNormal_8_8_VMX_4A ()
***************************************************************/
FORCE_INLINE void m3dDecompressNormal_8_8_VMX_4A (INT16 i_norm, m4dV *normOut)
{
   static const vmxV c_scl       = vmxVectSet( 1.f/181.f, 0.f,  1.f/181.f/181.f, 0.f);
   static const vmxV c_two       = vmxVectSet( 2.f,       0.f,  2.f,             0.f);
   static const vmxV c_minus_one = vmxVectSet(-1.f,       0.f, -1.f,             0.f);

   vmxV _norm = vmxVectSet(i_norm, 0, i_norm, 0);

   vmxV norm = vmxVectAbs(_norm); // vec_abs()
        norm = norm  * c_scl;
        norm = norm - vmxVectTrunc(norm); // vec_trunc();
        norm = vmxVectMadd(norm, c_two, c_minus_one);
   
   vmxV dot    = vmxVectDot(norm, norm);
        dot    = vmxVectMin(dot, vmxVectOne());
   vmxV norm_y = vmxVectSqrtEst(vmxVectOne() - dot);
   norm_y      = vmxVectSelect(norm_y, -norm_y, vmxVectLess(_norm, vmxVectZero()));
   norm        = vmxVectSetY(norm, vmxVectGetX(norm_y));

   vmxVectStoreA(norm, (float*)normOut);

#ifdef _DEBUG
   m3dV newNorm;
   m3dDecompressNormal_8_8(&i_norm, &newNorm);
   ASSERT(m3dIsEqualPoint_A(normOut, &newNorm, 0.0009f));
#endif
   return;
}

//
// End-of-file M3D_FLOAT2INT_VMX.H
//
